#####################################################################################
#
# google_analytics.pl
#   Interpret Google Analytics cookies
#   
# References:
#   Jon S. Nelson (http://www.dfinews.com/article/google-analytics-cookies-and-forensic-implications)
#
# Plugin Author: Ryan Benson (ryan@obsidianforensics.com)
#
#####################################################################################

package google_analytics;
use strict;
use URI::Escape;

my %config = (friendlyName   => "Google Analytics Cookie Parser",
              description    => "Parses Google Analytics cookies",
              artifactType   => "cookie",
              remoteLookups  => 0,
              browser        => "any",
              browserVersion => 0,
              version        => 20130718);

sub getConfig       { return %config; }
sub getFriendlyName { return $config{friendlyName}; }
sub getDescription  { return $config{description};; }
sub getArtifactType { return $config{artifactType}; }
sub getRemoteLookups{ return $config{remoteLookups}; }
sub getVersion      { return $config{version}; }

sub pluginmain{

    my $select_h = $::datastore->prepare(" SELECT rid,name,value,derived 
                                            FROM cookies 
                                            WHERE 
                                                name='__utma' OR 
                                                name='__utmb' OR
												name='__utmc' OR	
												name='__utmv' OR												
                                                name='__utmz'    ");

    my $update_h = $::datastore->prepare(" UPDATE cookies SET derived=? WHERE rid=? ");

    $select_h->execute();
    my $counter = 0;
    while (my @row = $select_h->fetchrow) {
        my $derived = $row[3];

		# Check if this plugin has already been run; if not, run it.  
		unless ($derived=~/\[Google Analytics Cookie\]/){
			if ($row[1] eq "__utma"){
				if ($row[2]=~/(\d+)\.(\d+)\.(\d{10})\.(\d{10})\.(\d{10})\.(\d+)/){
					$derived .= "Domain Hash: $1 | Unique Vistor ID: $2 | First Visit: " . ::EpochToHuman($3) . " | Previous Visit: " . ::EpochToHuman($4) . " | Last Visit: " . ::EpochToHuman($5) . " | Number of Sessions: $6 | [Google Analytics Cookie]";    }    
					$counter++;  }

			if ($row[1] eq "__utmb"){
				if ($row[2]=~/(\d+)\.(\d+)\.\d+\.(\d{10})/){
					$derived .= "Domain Hash: $1 | Pages Viewed: $2 | Last Visit: " . ::EpochToHuman($3) . " | [Google Analytics Cookie]";    }     
					$counter++;  }

			if ($row[1] eq "__utmc"){
				if ($row[2]=~/(\d+)/){
					$derived .= "Domain Hash: $1 | [Google Analytics Cookie]";    }     
					$counter++;  }
			
			if ($row[1] eq "__utmv"){
				if ($row[2]=~/(\d+)\.\|?(.*)/){
					$derived .= "Domain Hash: $1 | Custom Values: " . uri_unescape($2) . " [Google Analytics Cookie]";    }     
					$counter++;  }
					
			if ($row[1] eq "__utmz"){
				if ($row[2]=~/(\d+)\.(\d{10})\.(\d+)\.(\d+)/){
					$derived .= "Domain Hash: $1 | Last Time: " . ::EpochToHuman($2) . " | Sessions: $3 | Sources: $4 | ";
					my %parameters;
					my $raw_parameters = $row[2];
					$raw_parameters =~ s!^(\d+)\.(\d{10})\.(\d+)\.(\d+)\.!!;

					#Parse out cookie fields
					my @parameters = split /\|/, $raw_parameters;  # Split the cookie on the '|' delimiter
					foreach my $parameter (@parameters){
						$parameter =~ /(.+?)=(.+)/;       # and then split each parameter on the first '='
						$parameters{$1} = $2;  }          # and stick the parameter name and value in hash
        
					#Ex: 38950847.1357762586.5.5.utmcsr=google.com|utmccn=(referral)|utmcmd=referral|utmcct=/reader/view/
					if ($parameters{utmcmd}=~/referral/i){
						if ($parameters{utmcsr} && $parameters{utmcct}){
							$derived .= "Referrer: $parameters{utmcsr}$parameters{utmcct} | "; }
						if ($parameters{utmccn}!~/referral/i){
							$derived .= "Ad Campaign Info: $parameters{utmccn} | "; }
					}
					
					#Ex: 120910874.1368486805.1.1.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided)
					elsif ($parameters{utmcmd}=~/organic/i){
						$derived .= "Last Type of Access: $parameters{utmcmd} | ";
						if ($parameters{utmctr}){
							$derived .= "Search keywords: " . uri_unescape($parameters{utmctr}) . " | "; } 
						if ($parameters{utmccn}!~/organic/i){
							$derived .= "Ad Campaign Info: $parameters{utmccn} | "; }
					}
					
					#Ex: 27069237.1369840721.3.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
					elsif ($parameters{utmcmd}=~/none/i && $parameters{utmccn}=~/direct/){
						$derived .= "Last Type of Access: $parameters{utmccn} | ";
						if ($parameters{utmctr}){
							$derived .= "Search keywords: " . uri_unescape($parameters{utmctr}) . " | "; } 
					}

					# Otherwise, just print out all the fields					
					else {
						if ($parameters{utmcsr}){ $derived.= "Last Source Site: $parameters{utmcsr} | " };
						if ($parameters{utmccn}){ $derived.= "Ad Campaign Info: $parameters{utmccn} | " };
						if ($parameters{utmcmd}){ $derived.= "Last Type of Access: $parameters{utmcmd} | " };
						if ($parameters{utmctr}){ $derived.= "Keyword(s) from Search that Found Site: $parameters{utmctr} | " };
						if ($parameters{utmcct}){ $derived.= "Path to the page on the site of the referring link: $parameters{utmcct} | " };    					
					}
					
					$derived.= "[Google Analytics Cookie] ";    }
					$counter++;  }
			
			$update_h->execute( $derived, $row[0] );
		}
    }
    ::printStatus("$counter cookies parsed");
}

1;